/*

Design Pattern Automation Toolkit.
Application to create applications with emphasis on Design patterns.
And support for round trip engineering.
Copyright (C) 2004 Vineeth Neelkanat. nvineeth@gmail.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
using System;
using System.IO;
using DesignPatternDefinitions;
using System.Collections;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using CodeGenerators.Tags;

namespace CodeGenerators
{
	/// <summary>
	/// Abstract base class for dotnet codegenerators.
	/// the csharp , vb.net code generators just override 
	/// the abstract functions...
	/// </summary>
	public abstract class DotNetClassGenerator : IDPAClassGenerator
	{
		#region Fields
		private static CodeGeneratorOptions mGenOptions = null;
		private static CodeGeneratorOptions GenOptions
		{
			get
			{
				if( mGenOptions == null )
				{
					mGenOptions = new CodeGeneratorOptions();
					mGenOptions.BlankLinesBetweenMembers = true;
					mGenOptions.BracingStyle = "C";
					mGenOptions.ElseOnClosing = false;
				}
				return mGenOptions;
			}
		}
		#endregion

		#region IDPAClassGenerator Members

		public string GenerateClass(string dirname, IClass objClass, IProjectComponent parent)
		{
			string filename = dirname + "\\" + objClass.ClassName + this.Extension;
			GenerateCode(
				objClass,
				parent,
				filename
			);
			return filename;
		}


		#endregion
		
		#region Constructor / Initializers
		public DotNetClassGenerator()
		{
		}
		#endregion
		
		#region Implementation
		private CodeTypeDeclaration CreateClassTypeDeclaration(IClass iclass)
		{
			CodeTypeDeclaration ctypeDec = new CodeTypeDeclaration(iclass.ClassName);
			ctypeDec.Comments.Add(
				new CodeCommentStatement(
					new CodeComment(
				NDocTags.summary_begin+iclass.ClassSummary+
				NDocTags.summary_end,
				true)
				)
			);
			if(iclass.GetMetaInfo(Language).IndexOf("interface")!=-1) 
				ctypeDec.IsInterface = true;	
			else if(iclass.GetMetaInfo(Language).IndexOf("struct")!= -1) 
				ctypeDec.IsStruct = true;
			else if(iclass.GetMetaInfo(Language).IndexOf("enum")!= -1)
				ctypeDec.IsEnum = true;	

			if(iclass.Abstract)
				ctypeDec.TypeAttributes |= TypeAttributes.Abstract;

			return ctypeDec;
		}
		private void AddBaseClasses(CodeTypeDeclaration ctypeDec, IClass iclass )
		{
			ArrayList inhList = iclass.InheritedClasses;
			foreach(IInheritance inh in inhList) 
			{
				if(inh.BaseClassComponent == inh.DerivedClassComponent)
					ctypeDec.BaseTypes.Add(inh.BaseClass);
				else
					ctypeDec.BaseTypes.Add(inh.BaseClassComponent+"."+inh.BaseClass);
			}
		}
		private void AddAggregates(CodeTypeDeclaration ctypeDec, IClass iclass )
		{
			ArrayList AggregatesList = iclass.Aggregates;
			CodeMemberField cmField = null;
			CodeCommentStatement cmFieldComment = null;

			foreach(IAggregate agg in AggregatesList) 
			{
				string order = agg.Order.ToString();
				string aggFldName = agg.AggregateName;
				
				if(aggFldName != null && aggFldName == string.Empty)
					aggFldName = "noNameAggField" + AggregatesList.IndexOf(agg);
				
				if (order == "Single")
				{
					/* Add a Field */
					cmField = new CodeMemberField(agg.AggregateeClass,aggFldName);
					cmFieldComment = new CodeCommentStatement(
						new CodeComment("This field corressponds to the single aggregate relationship with "+agg.AggregateeClassComponent+"."+agg.AggregateeClass ,true)
					);
				}
				else
				{
					cmField = new CodeMemberField(typeof(System.Collections.ArrayList),aggFldName);
					cmFieldComment = new CodeCommentStatement(
						new CodeComment("This field corressponds to the multiple aggregate relationship with "+agg.AggregateeClassComponent+"."+agg.AggregateeClass ,true)
					);
				}
				cmField.Comments.Add(cmFieldComment);
				ctypeDec.Members.Add(cmField);
			}
		}
		private void AddReferences(CodeTypeDeclaration ctypeDec, IClass iclass )
		{
			ArrayList ReferencesList = iclass.References;
			if(ReferencesList.Count > 0)
			{
				CodeCommentStatement stmtref = new CodeCommentStatement(new CodeComment());
				stmtref.Comment.Text += " The following classes are referenced by this class";
				foreach(IReference ir in ReferencesList) 
				{
					stmtref.Comment.Text +="\n";
					stmtref.Comment.Text +=ir.ReferedComponentName+"."+ir.ReferedClassName;
				}
				ctypeDec.Comments.Add(stmtref);
			}
		}
		private void AddCreations(CodeTypeDeclaration ctypeDec, IClass iclass )
		{
			ArrayList CreatesList = iclass.Creations;
			if(CreatesList.Count > 0)
			{
				CodeCommentStatement stmtCrt = new CodeCommentStatement(new CodeComment());
				stmtCrt.Comment.Text += " The following classes are Created by this class";
				foreach(ICreation ic in CreatesList) 
				{
					stmtCrt.Comment.Text+="\n";
					stmtCrt.Comment.Text+=ic.CreateeComponentName+"."+ic.CreateeClassName;
				}
				ctypeDec.Comments.Add(stmtCrt);
			}
		}
		private void AddFields(CodeTypeDeclaration ctypeDec, IClass iclass )
		{
			ArrayList FieldsList = iclass.Fields;
				
			foreach(IField field in FieldsList) 
			{
				CodeMemberField clsField = new CodeMemberField();

				clsField.Name = field.FieldName;
				clsField.Type = this.GetCodeTypeRef(field.FieldType);
				
				clsField.Comments.Add(
					new CodeCommentStatement(
					NDocTags.summary_begin+field.FieldSummary+NDocTags.summary_end,
					true)
				);
				
				/* TODO : analyze the metainfo*/
				clsField.Attributes ^= clsField.Attributes; //clear it!
				
				switch (field.FieldVisibility.ToLower() )
				{
					case "private":
						clsField.Attributes |= MemberAttributes.Private;
						break;
					case "protected":
						//clsField.Attributes |= FieldAttributes.;
						break;
					case "public":
						clsField.Attributes |= MemberAttributes.Public;
						break;
				}
				ctypeDec.Members.Add(clsField);
			}
		}
		private void AddMethods(CodeTypeDeclaration ctypeDec, IClass iclass )
		{
			ArrayList MethodsList = iclass.Methods;
			foreach(IMethod method in MethodsList) 
			{
				if(method.GetMetaInfo(Language).IndexOf("property")!=-1) //property
				{
					CodeMemberProperty codeMemProp = new CodeMemberProperty();
					codeMemProp.Name = method.MethodName;
					codeMemProp.Type = this.GetCodeTypeRef(method.MethodReturnType);
					//get and set added by default.
					codeMemProp.HasGet = true;
					codeMemProp.HasSet = true;
					CodeCommentStatement clsMethdComment = new CodeCommentStatement(
						NDocTags.summary_begin+
						method.MethodSummary+NDocTags.summary_end,
						true);
					codeMemProp.Comments.Add(clsMethdComment);
					switch (method.MethodVisibility.ToLower() )
					{
						case "private":
							codeMemProp.Attributes |= MemberAttributes.Private;
							break;
						case "protected":
							//clsField.Attributes |= FieldAttributes.;
							break;
						case "public":
							codeMemProp.Attributes |= MemberAttributes.Public;
							break;
					}

					if(method.GetMetaInfo(Language).IndexOf("final")!=-1) 
						codeMemProp.Attributes |= MemberAttributes.Final;
					if(method.GetMetaInfo(Language).IndexOf("static")!=-1) 
						codeMemProp.Attributes |= MemberAttributes.Static;
					ctypeDec.Members.Add(codeMemProp);
				}
				else//method
				{
					CodeMemberMethod codeMemMethod = new CodeMemberMethod();
					CodeCommentStatementCollection codeComColl = new CodeCommentStatementCollection();
					codeMemMethod.Attributes = 0;//clear it.
					string lMethodComment;
					/* construct the comment */
					lMethodComment = NDocTags.summary_begin+
						method.MethodSummary+
						NDocTags.summary_end;
					
					codeMemMethod.Comments.Add(
						new CodeCommentStatement(lMethodComment,true)
						);
									
					if(method.MethodType == MethodType.Abstract)
						codeMemMethod.Attributes |= MemberAttributes.Abstract;
				
					codeMemMethod.Name = method.MethodName;
				
					/*if(method.MethodReturnType!="void")//done this bcos @void is output instead of void :( 
						codeMemMethod.ReturnType = new CodeTypeReference(method.MethodReturnType);
					*/
				
					codeMemMethod.ReturnType = this.GetCodeTypeRef(method.MethodReturnType);
									
					/* Access Modifiers*/
					ArrayList ParametersList = method.Parameters;
					foreach(IParameter parameter in ParametersList) 
					{
						CodeParameterDeclarationExpression codeParam = 
							new CodeParameterDeclarationExpression();
						codeParam.Name = parameter.ParameterName;
						codeParam.Type = this.GetCodeTypeRef(parameter.ParameterType);
						/* TODO : Handle MetaInfo of parameters*/
						codeMemMethod.Parameters.Add(codeParam);
						/* also add the param ndoc tag in comment */
						lMethodComment=NDocTags.GetParamStartTag(parameter.ParameterName);
						lMethodComment+=NDocTags.param_end;
						codeMemMethod.Comments.Add(
							new CodeCommentStatement(lMethodComment,true)
							);
						
					}
					/* add the returns ndoc tag */
					lMethodComment=NDocTags.returns_begin;
					lMethodComment+=NDocTags.returns_end;
					codeMemMethod.Comments.Add(
						new CodeCommentStatement(lMethodComment,true)
						);
					//codeMemMethod.Comments.AddRange(
					switch (method.MethodVisibility.ToLower() )
					{
						case "private":
							codeMemMethod.Attributes |= MemberAttributes.Private;
							break;
						case "protected":
							//clsField.Attributes |= FieldAttributes.;
							break;
						case "public":
							codeMemMethod.Attributes |= MemberAttributes.Public;
							break;
					}
					if(method.GetMetaInfo(Language).IndexOf("new")!=-1) 
						codeMemMethod.Attributes |= MemberAttributes.New;
					if(method.GetMetaInfo(Language).IndexOf("final")!=-1) 
						codeMemMethod.Attributes |= MemberAttributes.Final;
					if(method.GetMetaInfo(Language).IndexOf("static")!=-1) 
						codeMemMethod.Attributes |= MemberAttributes.Static;

	
					ctypeDec.Members.Add(codeMemMethod);
				}
			}
		}
		private void GenerateCode(IClass iclass, IProjectComponent parent, string filename)
		{
			CodeTypeDeclaration ctypeDec = CreateClassTypeDeclaration(iclass);
			AddBaseClasses( ctypeDec , iclass );
			AddAggregates( ctypeDec , iclass );
			AddReferences( ctypeDec , iclass );
			AddCreations( ctypeDec , iclass );
			AddFields( ctypeDec , iclass );
			AddMethods( ctypeDec , iclass );
	
			StreamWriter stream = File.CreateText(filename);
			OutputCode( stream , parent.ComponentName , ctypeDec );
			stream.Close();
		}
		private void OutputCode(StreamWriter stream, string componentName,CodeTypeDeclaration ctd)
		{
			CodeNamespace namespc = new CodeNamespace(componentName);
			namespc.Types.Add(ctd);
			namespc.Imports.Add(new CodeNamespaceImport("System"));
			this.CodeGenerator.GenerateCodeFromNamespace(namespc,stream,GenOptions);
		}
		private CodeTypeReference GetCodeTypeRef(string type)
		{
			Type t = null;
			switch(type)
			{
				case "void":
					t = typeof(void);
					break;
				case "byte":
					t = typeof(byte);
					break;
				case "int":
					t = typeof(int);
					break;
				case "uint":
					t = typeof(uint);
					break;
				case "short":
					t = typeof(short);
					break;
				case "long":
					t = typeof(long);
					break;
				case "float":
					t = typeof(float);
					break;
				case "double":
					t = typeof(double);
					break;
				case "string":
					t = typeof(string);
					break;
				case "char":
					t = typeof(char);
					break;
				case "bool":
					t = typeof(bool);
					break;
				default:
					t = Type.GetType(type);
					break;
			}
			try
			{
				if( t == null )
					return new CodeTypeReference(type);
				return new CodeTypeReference(t);
			}
			catch(Exception exp)
			{
				System.Windows.Forms.MessageBox.Show(exp.ToString());
				return new CodeTypeReference(type);
			}
		}
		
		#endregion
		
		#region Abstract Properties
		protected abstract string Extension
		{
			get;
		}
		abstract protected string Language
		{
			get;
		}
		abstract protected ICodeGenerator CodeGenerator
		{
			get;
		}
		#endregion

	}
}
